<script>
import { getLngLat, isSameLngLat } from '../js/AMapUtils.js';
import { AMapGeoCode } from '../js/AMapGeoCode.js';
import { isEqual, get } from 'lodash-es';
import icon_marker from '../../../assets/mark_bs.png';

export default {
  name: 'AMapPositionPicker',
  emits: ['update:modelValue', 'change'],
  props: {
    /* modelValue: { longitude: "",latitude: "", address: "" } */
    modelValue: { type: Object, default: () => ({}) },
    mapGetter: { type: Function, default: undefined },
    markerOptions: { type: [Object, Function], default: undefined },
    previewOnly: { type: Boolean, default: true },
    enableGeocode: { type: Boolean, default: false },
    replaceFields: { type: Object, default: () => ({}) },
  },
  computed: {
    fields() {
      const dft = { longitude: 'longitude', latitude: 'latitude', address: 'address' };
      return Object.assign(dft, this.replaceFields);
    },
  },
  created() {
    if (typeof this.mapGetter === 'function') {
      this.map = this.mapGetter();
    } else {
      throw new Error('mapGetter is not a function, please provide a function to return a map instance');
    }
    this.$watch(
      () => this.modelValue,
      (data) => {
        this.update(data);
      },
      { immediate: true, deep: true }
    );
    this.$watch(
      () => this.previewOnly,
      () => {
        this.map[this.previewOnly === true ? 'off' : 'on']('click', this.onMapClick, this);
      },
      { immediate: true }
    );
  },
  unmounted() {
    this.destroy();
  },
  methods: {
    async update(data) {
      const { longitude, latitude, address } = this.fields;
      const position = getLngLat([get(data, longitude), get(data, latitude)]);
      if (position && !isSameLngLat(position, this.position)) {
        this.position = position;
        this.addMarker(this.position);
      }
    },
    async onMapClick({ lnglat }) {
      const { longitude, latitude, address } = this.fields;
      this.position = getLngLat(lnglat);
      this.addMarker(this.position);
      this.map.setCenter(lnglat);
      const { address: addressText } = await this.geoCode({ position: this.position });
      this.output({ [longitude]: lnglat.lng, [latitude]: lnglat.lat, [address]: addressText });
    },
    async geoCode({ position, address }) {
      if (!!this.previewOnly || !this.enableGeocode) {
        return {};
      }
      if (!this.aMapGeoCode) {
        this.aMapGeoCode = new AMapGeoCode();
      }
      position = getLngLat(position);
      if (position) {
        const address = await this.aMapGeoCode.regeocode(position);
        return { address };
      }
      if (address) {
        const position = await this.aMapGeoCode.geocode(address);
        return { position };
      }
      return {};
    },
    addMarker(position) {
      if (this.marker) {
        this.map.remove(this.marker);
      }
      const option = typeof this.markerOptions === 'function' ? this.markerOptions() : this.markerOptions;
      const opt = {
        content: `<img src="${icon_marker}" style="width: 38px;height: 64px"/>`,
        offset: new AMap.Pixel(-19, -64),
      };
      const marker = new AMap.Marker(
        Object.assign(opt, option, {
          position: getLngLat(position),
        })
      );
      this.marker = marker;
      this.map.add(this.marker);
      this.map.setFitView(this.marker);
    },
    output(data) {
      if (!!this.previewOnly) return;
      data = Object.assign({}, this.modelValue, data);
      if (isEqual(data, this.modelValue)) {
        return;
      }
      this.$emit('update:modelValue', data);
      setTimeout(() => {
        this.$emit('change', data);
      }, 0);
    },
    destroy() {
      if (this.map) {
        if (this.marker) {
          this.map.remove(this.marker);
          this.marker = null;
        }
        this.map.off('click', this.onMapClick, this);
        this.map = null;
      }
    },
  },
  render() {
    return null;
  },
};
</script>
